# This is a sample commands.py.  You can add your own commands here.
#
# Please refer to commands_full.py for all the default commands and a complete
# documentation.  Do NOT add them all here, or you may end up with defunct
# commands when upgrading ranger.

# A simple command for demonstration purposes follows.
# -----------------------------------------------------------------------------

from __future__ import (absolute_import, division, print_function)

# You can import any python module as needed.
import os

# You always need to import ranger.api.commands here to get the Command class:
from ranger.api.commands import Command


# Any class that is a subclass of "Command" will be integrated into ranger as a
# command.  Try typing ":my_edit<ENTER>" in ranger!
#class my_edit(Command):
#    # The so-called doc-string of the class will be visible in the built-in
#    # help that is accessible by typing "?c" inside ranger.
#    """:my_edit <filename>
#
#    A sample command for demonstration purposes that opens a file in an editor.
#    """
#
#    # The execute method is called when you run this command in ranger.
#    def execute(self):
#        # self.arg(1) is the first (space-separated) argument to the function.
#        # This way you can write ":my_edit somefilename<ENTER>".
#        if self.arg(1):
#            # self.rest(1) contains self.arg(1) and everything that follows
#            target_filename = self.rest(1)
#        else:
#            # self.fm is a ranger.core.filemanager.FileManager object and gives
#            # you access to internals of ranger.
#            # self.fm.thisfile is a ranger.container.file.File object and is a
#            # reference to the currently selected file.
#            target_filename = self.fm.thisfile.path
#
#        # This is a generic function to print text in ranger.
#        self.fm.notify("Let's edit the file " + target_filename + "!")
#
#        # Using bad=True in fm.notify allows you to print error messages:
#        if not os.path.exists(target_filename):
#            self.fm.notify("The given file does not exist!", bad=True)
#            return
#
#        # This executes a function from ranger.core.acitons, a module with a
#        # variety of subroutines that can help you construct commands.
#        # Check out the source, or run "pydoc ranger.core.actions" for a list.
#        self.fm.edit_file(target_filename)
#
#    # The tab method is called when you press tab, and should return a list of
#    # suggestions that the user will tab through.
#    # tabnum is 1 for <TAB> and -1 for <S-TAB> by default
#    def tab(self, tabnum):
#        # This is a generic tab-completion function that iterates through the
#        # content of the current directory.

class FzfMarksBase(Command):

    fzf_cmd = os.environ.get("FZF_MARKS_CMD", "fzf")
    # https://github.com/urbainvaes/fzf-marks
    bookmark_file = os.environ.get("FZF_MARKS_FILE") or os.path.join(
        os.environ.get("HOME", os.path.expanduser("~")), ".fzf-marks"
    )


class fmark(FzfMarksBase):
    """
    :fmark <name>
    Mark the current directory with provided keyword
    """

    def execute(self):
        if not self.arg(1):
            self.fm.notify(
                "A keyword must be given for the current bookmark!", bad=True
            )
            return

        item = "{} : {}".format(self.arg(1), self.fm.thisdir.path)

        if not os.path.exists(self.bookmark_file):
            with open(self.bookmark_file, "a") as f:
                pass

        with open(self.bookmark_file, "r") as f:
            for line in f.readlines():
                if line.split(":")[1].strip() == self.fm.thisdir.path:
                    self.fm.notify(
                        "Fzf bookmark already exists: {}".format(line.strip()), bad=True
                    )
                    return

        with open(self.bookmark_file, "a") as f:
            f.write("{}{}".format(item, os.linesep))
            self.fm.notify("Fzf bookmark has been added: {}".format(item))


class dmark(FzfMarksBase):
    """
    dmark: delete current directory from fzf-marks file
    """

    fzf_opts = os.environ.get(
        "FZF_DMARK_OPTS",
        "--cycle -m --ansi --bind=ctrl-o:accept,ctrl-t:toggle",
    )

    def execute(self):
        import subprocess

        items = None
        query = ""

        if self.arg(1):
            query = self.arg(1)

        if not os.path.exists(self.bookmark_file):
            self.fm.notify("No fzf bookmark is created yet!", bad=True)
            return

        # TODO: batch deletion
        command = '< "{2}" sort -f | {0} {1} --query="{3}"'.format(
            self.fzf_cmd, self.fzf_opts, self.bookmark_file, query
        )

        process = self.fm.execute_command(
            command, universal_newlines=True, stdout=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        if process.returncode == 0:
            items = stdout.rstrip().split("\n")

        if not items:
            return

        with open(self.bookmark_file, "r") as f:
            lines = f.readlines()

        with open(self.bookmark_file, "w") as f:
            for line in lines:
                if line.strip() not in items:
                    f.write(line)

        self.fm.notify("Fzf bookmark is deleted: {}".format(", ".join(items)))


class fzm(FzfMarksBase):
    """
    fzm: select and jump to bookmark stored in fzf-marks
    """

    fzf_opts = os.environ.get(
        "FZF_FZM_OPTS",
        "--cycle +m --ansi --bind=ctrl-o:accept,ctrl-t:toggle --select-1",
    )

    def execute(self):
        import subprocess

        target = None
        query = ""

        if self.arg(1):
            query = self.arg(1)

        if not os.path.exists(self.bookmark_file):
            self.fm.notify("No fzf bookmark is created yet!", bad=True)
            return

        command = '< "{2}" sort -f | {0} {1} --query "{3}"'.format(
            self.fzf_cmd, self.fzf_opts, self.bookmark_file, query
        )

        process = self.fm.execute_command(
            command, universal_newlines=True, stdout=subprocess.PIPE
        )
        stdout, stderr = process.communicate()
        if process.returncode == 0:
            key, target = stdout.rstrip().split(" : ", 1)
            target = os.path.expanduser(target)

        if not target:
            return
        elif os.path.isdir(target):
            self.fm.cd(target)
        elif os.path.isfile(target):
            self.fm.select_file(target)
        else:
            self.fm.notify(
                "Invalid fzf bookmark location: {} : {}".format(key, target), True
            )

class fzf_select(Command):
    """
    :fzf_select
    Find a file using fzf.
    With a prefix argument select only directories.
    See: https://github.com/junegunn/fzf
    """
    def execute(self):
        import subprocess
        import os.path
        if self.quantifier:
            # match only directories
            command = "find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
            -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"

        else:
            # match files and directories
            command = "find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
            -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"

        fzf = self.fm.execute_command(command,
                                      universal_newlines=True,
                                      stdout=subprocess.PIPE)
        stdout, stderr = fzf.communicate()
        if fzf.returncode == 0:
            fzf_file = os.path.abspath(stdout.rstrip('\n'))
            if os.path.isdir(fzf_file):
                self.fm.cd(fzf_file)
            else:
                self.fm.select_file(fzf_file)
